#pragma once

#include <vector>

#include "../math/collision.h"
#include "../math/transform3f.h"

namespace ray_tracing
{
	enum class hit_param : int
	{
		first_hit,
		last_hit
	};

	class material_t;

	typedef std::vector<class scene_object_t*> scene_nodes_t;

	class insertion_error{};

	class scene_object_t
	{
		friend class scene_t;
	protected:
		virtual ~scene_object_t();
		scene_object_t(scene_object_t* in_parent, material_t* in_material);
	public:
		virtual collision::collision_info_t intersect_with(const ray3f& ray, hit_param param) = 0;
	public:
		//complexity: O(c*p), c - num childs, p - num parents
		//throws: insertion_error
		void add_child_safe(scene_object_t* child);

		/*description:
			use it if you sure that child holds following conditions:

			this != child && lookup_children(child) == false
				&& parent != child && lookup_hierarchy_up(child) == false

			otherwise you'll get cyclic oriented graph
		*/

		//complexity: O(1), in debug O(c*p), c - num childs, p - num parents
		void add_child_fast(scene_object_t* child);

		transform3f get_world_matrix() const;
		const scene_nodes_t& get_children() const;
		void detach_from_parent();
	private:
		void set_new_parent(scene_object_t* new_parent);

	private:
		scene_object_t* parent;
		scene_nodes_t children;
	public:
		transform3f local_SRT;
		class material_t* material;
	private:
		friend void remove_child(scene_object_t * in, scene_object_t * what);
		friend bool lookup_children(scene_object_t * in, scene_object_t * what);
		friend bool lookup_hierarchy_up(scene_object_t * in, scene_object_t * what);
	};

	//removes child from in, but don't delete it.
	void remove_child(scene_object_t * in, scene_object_t * what);
	bool lookup_children(scene_object_t * in, scene_object_t * what);
	bool lookup_hierarchy_up(scene_object_t * in, scene_object_t * what);
}